package com.hotec.PROJECT_NAME.config.security.filter;

import com.google.common.collect.Sets;
import com.hotec.PROJECT_NAME.component.basic.ConstantKey;
import com.hotec.PROJECT_NAME.component.exception.exc.TokenException;
import com.hotec.PROJECT_NAME.modules.sys.entity.SysAccount;
import com.hotec.PROJECT_NAME.modules.sys.entity.SysUrlLog;
import com.hotec.PROJECT_NAME.modules.sys.service.SysAccountService;
import com.hotec.PROJECT_NAME.modules.sys.service.SysUrlLogService;
import com.hotec.PROJECT_NAME.utils.UtilsJwt;
import com.hotec.PROJECT_NAME.utils.UtilsLog;
import com.hotec.PROJECT_NAME.utils.UtilsSpring;
import com.hotec.PROJECT_NAME.config.security.impl.GrantedAuthorityImpl;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.MalformedJwtException;
import io.jsonwebtoken.SignatureException;
import io.jsonwebtoken.UnsupportedJwtException;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.env.Environment;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * token的校验
 * 该类继承自BasicAuthenticationFilter，在doFilterInternal方法中，
 * 从http头的Authorization 项读取token数据，然后用Jwts包提供的方法校验token的合法性。
 * 如果校验通过，就认为这是一个取得授权的合法请求
 *
 * @author 黑山老妖
 */
public class JwtAuthenticationFilter extends BasicAuthenticationFilter {
    private ExecutorService pool = Executors.newFixedThreadPool(10);

    private Set<String> NO_RECORD_URL = Sets.newHashSet(
            "/sys/account/info",
            "/sys/resource/navigate",
            "/sys/attachment/upload"

    );

    private static final Logger logger = LoggerFactory.getLogger(JwtAuthenticationFilter.class);

    public JwtAuthenticationFilter(AuthenticationManager authenticationManager) {
        super(authenticationManager);
    }


    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
        String header = request.getHeader(ConstantKey.TOKEN_NAME);

        if (header == null || !header.startsWith(ConstantKey.TOKEN_PRE)) {
            chain.doFilter(request, response);
            return;
        }
        MyHttpServletRequestWrapper requestWrapper = new MyHttpServletRequestWrapper(request);
        UsernamePasswordAuthenticationToken authentication = getAuthentication(request);
        SecurityContextHolder.getContext().setAuthentication(authentication);
        pool.submit(() -> {
            try {
                String s = requestWrapper.getRequestURL().toString();
                if ((!s.contains("get") && !s.contains("query"))) {
                    if (!NO_RECORD_URL.contains(requestWrapper.getServletPath())) {
                        SecurityContextHolder.getContext().setAuthentication(authentication);
                        System.out.println(s);
                        BufferedReader br = null;

                        br = requestWrapper.getReader();

                        String str, wholeStr = "";
                        while ((str = br.readLine()) != null) {
                            wholeStr += str;
                        }
                        SysUrlLogService sysUrlLogService = UtilsSpring.getBean(SysUrlLogService.class);
                        SysUrlLog sysUrlLog = new SysUrlLog();
                        sysUrlLog.setContent(s);
                        sysUrlLog.setRemark(wholeStr);
                        sysUrlLogService.save(sysUrlLog);
                    }
                }
            } catch (IOException e) {
                UtilsLog.log(this.getClass(), e);
            }

        });
        chain.doFilter(requestWrapper, response);
    }

    private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request) {
        String token = request.getHeader(ConstantKey.TOKEN_NAME);
        if (token != null) {
            String accountId;
            try {
                accountId = (String) UtilsJwt.get(token, ConstantKey.ACCOUNT_ID);
                List<LinkedHashMap<String, Object>> roles = (List<LinkedHashMap<String, Object>>) UtilsJwt.get(token, "roles");

                ArrayList<GrantedAuthority> authorities = new ArrayList<>();
                if (roles != null) {
                    roles.stream().forEach(item -> {
                        authorities.add(new GrantedAuthorityImpl((String) item.get("authority")));
                    });
                }
                if (accountId != null) {
                    //TODO 需要改成字典项在后台开启
                    String slp = UtilsSpring.getBean(Environment.class).getProperty("sing_login_place", "0");
                    if (!StringUtils.equalsIgnoreCase(slp, ConstantKey.SING_LOGIN_PLACE)) {
                        SysAccountService sysAccountService = UtilsSpring.getBean(SysAccountService.class);
                        SysAccount account = sysAccountService.get(accountId);
                        if (Objects.nonNull(account)) {
                            String slpStr = UtilsJwt.get(token, "slp") + "";
                            String check = (account.getLastLoginTime().getTime() + "");
                            if (null == slpStr || !StringUtils.equalsIgnoreCase(slpStr, check)) {
                                throw new TokenException("账号在其他地方登陆，密码已泄露，建议重置并重新登录！");
                            }
                        }
                    }
                    return new UsernamePasswordAuthenticationToken(accountId, null, authorities);
                }
            } catch (ExpiredJwtException e) {
                logger.error("Token已过期: {} " + e);
                throw new TokenException("Token已过期");
            } catch (UnsupportedJwtException e) {
                logger.error("Token格式错误: {} " + e);
                throw new TokenException("Token格式错误");
            } catch (MalformedJwtException e) {
                logger.error("Token没有被正确构造: {} " + e);
                throw new TokenException("Token没有被正确构造");
            } catch (SignatureException e) {
                logger.error("签名失败: {} " + e);
                throw new TokenException("TOKEN签名失败");
            } catch (IllegalArgumentException e) {
                logger.error("非法参数异常: {} " + e);
                throw new TokenException("非法参数异常");
            }
        }
        throw new TokenException("用户信息未找到，请重新登录获取用户信息！");
    }
}
